﻿using System.Web;
using IntoTheVoid.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices.ComTypes;
using System.Diagnostics.Eventing.Reader;
using static System.Net.WebRequestMethods;
using System.Runtime.InteropServices;
using System.Windows.Documents;
using System.Security.Policy;
using ImTools;
using System.Threading;
using DryIoc;
using System.Windows.Shapes;

namespace IntoTheVoid.Units
{
	public class DownLoadManager
	{
		private const int FAIL_MAX_TIME = 10;
		private string fileUrl;
		private string fileName;
		private int failureTime;
		private bool pause = false;
		public long DownloadedSize { get; private set; }
		private int MaxSpeed = 0;
		private bool SpeedChange = false;
		private long serverFileLength = 0;

		public event EventHandler<AsyncCompletedEventArgs> DownloadFileCompleted;
		public event EventHandler<DownloadProgressChangedEventArgs> DownloadProgressChanged;
		public event EventHandler<DownloadStartedEventArgs> DownloadStarted;
		public event EventHandler<DownloadStartedReadyEventArgs> DownloadStartedReady;

		public DownLoadManager(string fileUrl, string fileName)
		{
			this.fileUrl = fileUrl;
			this.fileName = fileName;
			failureTime = 0;
			pause = false;
		}
		static public long GetDownLoadFileSize(string fileUrl)
		{
			long size = 0;
			size = GetHttpLength(fileUrl);
			return size;
		}

		public async Task<bool> DownloadFileAsync()
		{
			bool flag = false;
			int timePev = Environment.TickCount, timeNow = Environment.TickCount;
			var sourceFileUrl = System.Web.HttpUtility.UrlPathEncode(this.fileUrl);

			if (!Directory.Exists(System.IO.Path.GetDirectoryName(this.fileName)))
			{
				Directory.CreateDirectory(System.IO.Path.GetDirectoryName(this.fileName));
			}
			if (System.IO.File.Exists(this.fileName))
			{
				DownloadedSize = new FileInfo(fileName).Length;
				LogHelper.Info("File Exist Size:" + DownloadedSize + "FileName:" + fileName);
			}
			else
			{
				LogHelper.Info("File Not Exist:" + fileName);
				DownloadedSize = 0;
			}

			DownloadStartedReady?.Invoke(this, new DownloadStartedReadyEventArgs(this.failureTime + 1));

			serverFileLength = GetHttpLength(this.fileUrl);
			if (serverFileLength == -1L)
			{
				DownloadFileCompleted?.Invoke(this, new AsyncCompletedEventArgs(new Exception(this.fileUrl), false, null));
				return false;
			}

			if (DownloadedSize == serverFileLength)
			{
				// 文件是完整的，直接结束下载任务
				LogHelper.Info("File Is Complete" + fileName + "File Exist Size:" + DownloadedSize);
				DownloadFileCompleted?.Invoke(this, new AsyncCompletedEventArgs(null, true, fileName));
				return true;
			}

			DownloadStarted?.Invoke(this, new DownloadStartedEventArgs(this.fileName, serverFileLength));
			do
			{
				flag = await DownloadFile(sourceFileUrl, this.fileName);
			} while (!flag && failureTime <= FAIL_MAX_TIME && !pause);

			if (!flag && this.failureTime > FAIL_MAX_TIME)
			{
				LogHelper.Info("Download failureTime is Max:" + this.fileUrl);
				DownloadFileCompleted?.Invoke(this, new AsyncCompletedEventArgs(new Exception("failureTimes full"), false, null));
			}

			if (flag && !pause)
			{
				DownloadFileCompleted?.Invoke(this, new AsyncCompletedEventArgs(null, true, fileName));
			}
			return flag;
		}
		private async Task<bool> DownloadFile(string url, string path)
		{
			bool flag = false;
			int timePev = Environment.TickCount, timeNow = Environment.TickCount;
			try
			{
				LogHelper.Info("Start DownloadFile Request");
				HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
				request.Timeout = 10000;
				request.ReadWriteTimeout = 10000;
				request.Method = "GET";
				request.Proxy = null;
				request.AddRange(DownloadedSize);
				long lastTransferredBytesCount = DownloadedSize;

				DownloadStartedReady?.Invoke(this, new DownloadStartedReadyEventArgs(this.failureTime + 1));
				using (var response = await request.GetResponseAsync())
				{
					LogHelper.Info("Get Response request.GetResponseAsync ");
					using (var responseStream = response.GetResponseStream())
					{
						// 限速
						ThrottledStream throttledStream = new ThrottledStream(responseStream, MaxSpeed <= 0 ? ThrottledStream.Infinite : MaxSpeed);
						using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
						{
							byte[] bArr = new byte[4096];
							int size = await throttledStream.ReadAsync(bArr, 0, bArr.Length); //responseStream.Read(bArr, 0, (int)bArr.Length);

							while (size > 0)
							{
								if (SpeedChange)
								{
									LogHelper.Info("DownloadFile SpeedChange");
									throttledStream.MaximumBytesPerSecond = MaxSpeed <= 0 ? ThrottledStream.Infinite : MaxSpeed * 1024;
									SpeedChange = false;
								}
								if (pause)
								{
									LogHelper.Info("DownloadFile Pause");
									DownloadProgressChanged?.Invoke(this, new DownloadProgressChangedEventArgs(DownloadedSize, serverFileLength, 0));
									break;
								}
								await stream.WriteAsync(bArr, 0, size);
								DownloadedSize += size;
								timeNow = Environment.TickCount;
								if (timeNow - timePev >= 1000)
								{
									var speed = (DownloadedSize - lastTransferredBytesCount) * 1000 / (timeNow - timePev);
									lastTransferredBytesCount = DownloadedSize;
									timePev = timeNow;
									DownloadProgressChanged?.Invoke(this, new DownloadProgressChangedEventArgs(DownloadedSize, serverFileLength, speed));
								}
								size = await throttledStream.ReadAsync(bArr, 0, bArr.Length); // responseStream.Read(bArr, 0, (int)bArr.Length);
							}
							await stream.FlushAsync();
							stream.Close();
							throttledStream.Close();


						}
					}
				}
				if (!pause && DownloadedSize == serverFileLength)
				{
					flag = true;
					// DownloadFileCompleted?.Invoke(this, new AsyncCompletedEventArgs(null, true, fileName));
				}
			}
			catch (Exception ex)
			{
				var error = ex.ToString();
				this.failureTime++;
				LogHelper.Error(ex, "DownloadFile throttledStream.ReadAsync Error" + error + "failureTime:" + failureTime);
				if (failureTime <= FAIL_MAX_TIME)
				{
					LogHelper.Info("DownloadFile Try again");
				}
			}
			return flag;
		}



		static private long GetHttpLength(string url)
		{
			long length = -1L;
			try
			{
				var sourceFileUrl = System.Web.HttpUtility.UrlPathEncode(url);
				var webRequest = (HttpWebRequest)WebRequest.Create(sourceFileUrl);
				webRequest.Method = "HEAD";
				webRequest.Timeout = 10000;
				webRequest.Credentials = CredentialCache.DefaultCredentials;
				webRequest.Proxy = WebRequest.DefaultWebProxy;

				using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
				{
					if (response.StatusCode == HttpStatusCode.OK)
					{
						length = response.ContentLength;
					}
					if (length <= 0)
						throw new Exception();
				}
				return length;
			}
			catch (Exception ex)
			{
				LogHelper.Error(ex, "DownloadFile GetHttpLength  response.ContentLength:" + length);
				return length;
			}
		}

		public void ResetFailureTime()
		{
			failureTime = 0;
		}

		// 暂停
		public void SetPause()
		{
			pause = true;
		}

		// 限速
		public void SetSpeed(int Speed)
		{
			SpeedChange = true;
			MaxSpeed = Speed;
		}

	}
}
